package hash

// nolint:gosec
import (
	"crypto/md5"
	"crypto/sha1"
	"crypto/sha256"
	"crypto/sha512"
	"errors"
	"fmt"
	"hash"
	"io"
	"os"

	"gitee.com/xuender/toolkit/pb"
)

// Read 读取生成摘要.
func Read(reader io.Reader, hashType pb.HashType) (string, error) {
	w := getHash(hashType)
	bs := make([]byte, 1024)

	for {
		n, err := reader.Read(bs)
		if err != nil {
			if errors.Is(err, io.EOF) {
				break
			}

			return "", err
		}

		if n == 0 {
			break
		}

		w.Write(bs[:n])
	}

	return fmt.Sprintf("%x", w.Sum(nil)), nil
}

// File 文件生成摘要.
func File(file string, hashType pb.HashType) (string, error) {
	f, err := os.Open(file)
	if err != nil {
		return "", err
	}
	defer f.Close()

	return Read(f, hashType)
}

// FileHash 文件全摘要.
func FileHash(file string) map[string]string {
	f, err := os.Open(file)
	if err != nil {
		return map[string]string{"error": err.Error()}
	}
	defer f.Close()

	hashs := map[string]hash.Hash{}
	for k, v := range pb.HashType_value {
		hashs[k] = getHash(pb.HashType(v))
	}

	bs := make([]byte, 1024)

	for {
		n, err := f.Read(bs)
		if err != nil {
			if errors.Is(err, io.EOF) {
				break
			}

			return map[string]string{"error": err.Error()}
		}

		if n == 0 {
			break
		}

		for _, w := range hashs {
			w.Write(bs[:n])
		}
	}

	ret := map[string]string{}
	for k, w := range hashs {
		ret[k] = fmt.Sprintf("%x", w.Sum(nil))
	}

	return ret
}

// Type 摘要类型.
func Type(t string) pb.HashType {
	return pb.HashType(pb.HashType_value[t])
}

func getHash(hashType pb.HashType) hash.Hash {
	switch hashType {
	case pb.HashType_sha1:
		// nolint: gosec
		return sha1.New()
	case pb.HashType_md5:
		// nolint: gosec
		return md5.New()
	case pb.HashType_sha256:
		return sha256.New()
	case pb.HashType_sha512:
		return sha512.New()
	default:
		// nolint: gosec
		return md5.New()
	}
}
